/**
 * XappTool Web SDK
 * 专为移动端H5优化的轻量级追踪SDK
 * @version 1.0.0
 */
class XappTool {
    constructor() {
        this.version = '1.0.0';
        this.api_url = 'https://api.xapptool.cn';
    }
    // common init
    async commonInit(options){
        let appKey = options.app_key || ''
        let debug = options.debug || false
        if(!appKey){
            throw new Error('app_key is required');
        }
        this.appKey = appKey
        this.debug = debug
        this.fingerprint = this.md5Core(this.getCanvasFingerprint())
        this.customInfo = this.getUrlParams()
        this.deviceInfo =  this.getDeviceInfo()
    }
    // web init
    async init(options) {
        await this.commonInit(options)
	    this.custom = await this.getCustomInfo()
	    return this.custom
    }
    // app init
    async register() {
		this.custom = await this.getCustomInfo()
        return this
    }
    // app init
    async trace(appKey,eventName,data) {
        await this.commonInit(appKey)
        this.trackEvent(eventName,data)
        return this
    }
    // 获取应用信息
    async getAppAsyncInfo(){
        let path  =  '/api/device/info'
        let data = {deviceInfo: this.deviceInfo, customInfo: this.customInfo};
        let res = await this.syncAjaxRequest('POST', path, data)
        return res.data || {}
    }
    // 获取自定义数据
    async getCustomInfo(){
        let path  =  '/api/device/custom'
        let data = {deviceInfo: this.deviceInfo};
        let res = await this.syncAjaxRequest('POST', path, data)
        return res.data.custom || {}
    }
    // now
    now() {
        return new Date().getTime();
    }
    // 获取设备信息
    getDeviceInfo() {
        var device = this.detectDevice();
        return {
            // 基础信息
            platform: device.isIOS ? 'iOS' : (device.isAndroid ? 'Android' : 'Web'),
            isWechat: device.isWechat,
            isQQ: device.isQQ,
            isAlipay: device.isAlipay,

            isIOS: device.isIOS,
            isAndroid: device.isAndroid,
            isHuawei: device.isHuawei,
            isXiaomi: device.isXiaomi,
            isOPPO: device.isOPPO,
            isVivo: device.isVivo,
            isHonor: device.isHonor,
            isYyb: device.isYyb,

            screenWidth: window.screen.width,
            screenHeight: window.screen.height,
            pixelRatio: window.devicePixelRatio || 1,
            language: navigator.language,
            userAgent: navigator.userAgent,
            url: window.location.href,
            referrer: document.referrer,
            timestamp: this.now(),


            // 硬件信息
            cpuCores: navigator.hardwareConcurrency || 0,  // CPU核心数
            memory: navigator.deviceMemory || 0,           // 设备内存
            platform: navigator.platform,                  // 操作系统平台
            vendor: navigator.vendor,                      // 设备制造商
            
            // 网络信息
            networkType: (navigator.connection || {}).type || '',  // 网络类型
            effectiveType: (navigator.connection || {}).effectiveType || '', // 网络速度
        };
    }

    // 检测设备类型
    detectDevice() {
        const userAgent = navigator.userAgent || '' ;

        const isIOS = /iPhone|iPad|iPod/i.test(userAgent)
        const isAndroid = /Android/i.test(userAgent)
        const isWechat = /MicroMessenger/i.test(userAgent)
        const isQQ = /QQ\//i.test(userAgent)
        const isAlipay = /AlipayClient/i.test(userAgent)
        const isMobile = /Mobile|Android|iPhone/i.test(userAgent)
        const isHuawei = userAgent.includes("Huawei") && platform === "Linux";
        const isXiaomi = userAgent.includes("Xiaomi") && platform === "Linux";
        const isOppo = userAgent.includes("OPPO") && platform === "Linux";
        const isVivo = userAgent.includes("vivo") && platform === "Linux";
        const isHonour = userAgent.match(/honor/i) == "honor" && platform === "Linux";
        return {
            isIOS: isIOS,
            isAndroid: isAndroid,
            isWechat: isWechat,
            isQQ: isQQ,
            isAlipay: isAlipay,
            isMobile: isMobile,
            isHuawei: isHuawei,
            isXiaomi: isXiaomi,
            isOppo: isOppo,
            isVivo: isVivo,
            isHonour: isHonour,
            is_yyb: isAndroid && isWechat
        };
    }

    // 发送同步请求
    syncAjaxRequest(method, path, data) {
        if(!this.debug){
            data = this.encryptObjectValues(data)
        }
        return new Promise((resolve, reject) => {
            let url = `${this.api_url}${path}`
            // 创建 XMLHttpRequest 对象
            var xhr = new XMLHttpRequest();
            // 打开请求，设置请求方法和 URL，第三个参数设为 false 表示同步请求
            xhr.open(method, url, false);
            xhr.setRequestHeader("Xapptool-Version",this.version)
            xhr.setRequestHeader('Xapptool-Appkey', this.appKey);
            xhr.setRequestHeader('Xapptool-debug', this.debug ? '1' : '0');
            xhr.setRequestHeader('Xapptool-Fingerprint', this.fingerprint);
            // 根据请求方法设置请求头（如果是 POST 请求，设置合适的 Content-Type）
            if (method === "POST") {
                xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
            }
            try {
                // 发送请求，如果有数据则将数据转换为 JSON 字符串后发送（对于 POST 请求）
                xhr.send(data? JSON.stringify(data) : null);
                var response = JSON.parse(xhr.responseText);
                if(!this.debug){
                    response = this.decryptObjectValues(response)
                }
 
                if (xhr.status >= 200 && xhr.status < 300) {
                    // 请求成功，将响应文本解析为 JSON（如果是 JSON 格式的话）并返回解析后的数据
                    try {
                        resolve(response);
                    } catch (e) {
                        // 如果解析 JSON 失败，直接返回响应文本
                        resolve(xhr.responseText);
                    }
                } else {
                    // 请求失败，拒绝 Promise，并传递错误信息
                    reject(new Error(xhr.statusText));
                }
            } catch (error) {
                // 发送请求过程中出现其他错误，同样拒绝 Promise，并传递错误信息
                reject(new Error(error));
            }
        });
    }
    // 解析URL参数
    parseQuery(query) {
        var params = {};
        if (!query) return params;
        
        var pairs = query.split('&');
        for (var i = 0; i < pairs.length; i++) {
            var pair = pairs[i].split('=');
            try {
                params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
            } catch(e) {
                console.error('URL参数解析错误:', e);
            }
        }
        return params;
    }
    // 获取URL参数
    getUrlParams() {
        var search = window.location.search;
        return this.parseQuery(search.substring(1));
    }
    // 追踪事件
    trackEvent(eventName, eventData) {
        if (!eventName) {
            throw new Error('事件名称不能为空');
        }
        var data = {
            type: 'event',
            deviceInfo: this.deviceInfo,
            urlParams: this.getUrlParams(),
            eventName: eventName,
            eventData: eventData || {},
            timestamp: this.now()
        };
        this.syncAjaxRequest('POST','/api/device/event', data);
    }
    // 跳转到应用市场
    redirectToStore() {
        let deviceInfo = this.deviceInfo
        let appInfo = this.appInfo;
        let storeUrl = '';
        if (deviceInfo.isIOS) {
            if(appInfo.ios_scheme_url){
                // 追踪跳转事件
                this.trackEvent('redirect',{});
                // 跳转
                window.location.href = (appInfo.ios_scheme_url)
            }
            if(appInfo.appstore_down_url) {
                storeUrl = appInfo.appstore_down_url;
            }else{
                storeUrl = appInfo.down_url;
            }
        } else if (deviceInfo.isAndroid) {
            if(appInfo.android_scheme_url){
                // 追踪跳转事件
                this.trackEvent('redirect',{});
                // 跳转
                window.location.href = (appInfo.android_scheme_url)
            }
            if(deviceInfo.isHuawei && appInfo.is_huawei > 0 && appInfo.huawei_down_url){
                storeUrl = appInfo.huawei_down_url;
            }
            if(deviceInfo.isXiaomi && appInfo.is_xiaomi > 0 && appInfo.xiaomi_down_url){
                storeUrl = appInfo.xiaomi_down_url;
            }
            if(deviceInfo.isOppo && appInfo.is_oppo > 0 && appInfo.oppo_down_url){
                storeUrl = appInfo.oppo_down_url;
            }
            if(deviceInfo.isVivo && appInfo.is_vivo > 0 && appInfo.vivo_down_url){
                storeUrl = appInfo.vivo_down_url;
            }
            if(deviceInfo.isHonour && appInfo.is_honour > 0 && appInfo.honour_down_url){
                storeUrl = appInfo.honour_down_url;
            }
            if ((deviceInfo.isWechat || deviceInfo.isQQ) && deviceInfo.isAndroid && appInfo.is_yyb && appInfo.yyb_down_url) {
                storeUrl = appInfo.yyb_down_url;
            }
            if(!storeUrl && appInfo.googleplay_down_url){
                storeUrl = appInfo.googleplay_down_url;
            }
            if(!storeUrl && appInfo.down_url){
                storeUrl = appInfo.down_url;
            }
        } else {
            // 其他平台使用通用下载页
            storeUrl = appInfo.down_url;
        }
        if(storeUrl){
            // 追踪跳转事件
            this.trackEvent('redirect', {
                store_url: storeUrl,
            });
            // 跳转
            location.href = storeUrl;
        }else{
            alert('暂无下载链接')
        }
    }
    // 获取canvas指纹
    getCanvasFingerprint() {
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        ctx.textBaseline = "top";
        ctx.font = "14px 'Arial'";
        ctx.textBaseline = "alphabetic";
        ctx.fillStyle = "#f60";
        ctx.fillRect(125, 1, 62, 20);
        ctx.fillStyle = "#069";
        ctx.fillText("Hello, world!", 2, 15);
        ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
        ctx.fillText("Hello, world!", 4, 17);
        var b64 = canvas.toDataURL().replace("data:image/png;base64,","");
        return b64;
    }
    // 解析参数
    getUrlParams() {
        var params = {};
        var queryString = window.location.search.substring(1);
        var vars = queryString.split("&");
        for (var i = 0; i < vars.length; i++) {
            var pair = vars[i].split("=");
            params[pair[0]] = pair[1];
        }
        return params;
    }
    // 定义一些辅助函数
    md5_leftRotate(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }
    // 定义 MD5 哈希函数
    md5_add(x, y) {
        var lsw = (x & 0xFFFF) + (y & 0xFFFF);
        var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
        return (msw << 16) | (lsw & 0xFFFF);
    }
    // 定义 MD5 哈希函数
    md5Core(message) {
        // 初始化一些常量
        var rotateAmounts = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
            5, 9, 14, 19, 5, 9, 14, 19, 5, 9, 14, 19, 5, 9, 14, 19,
            4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
            6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
        ];

        var constants = [
            0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
            0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
            0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
            0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
            0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
            0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
            0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
            0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
            0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
            0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
            0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
            0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
            0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
            0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
            0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
            0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
        ];

        var a = 0x67452301;
        var b = 0xefcdab89;
        var c = 0x98badcfe;
        var d = 0x10325476;

        var messageLength = message.length;
        var messageArray = [];
        for (var i = 0; i < messageLength; i++) {
            messageArray.push(message.charCodeAt(i));
        }

        var bitLength = messageLength * 8;
        messageArray.push(0x80);
        while (messageArray.length % 64!== 56) {
            messageArray.push(0);
        }

        var bitLengthArray = [];
        for (var i = 0; i < 8; i++) {
            bitLengthArray.push(bitLength >>> (i * 8) & 0xFF);
        }
        messageArray = messageArray.concat(bitLengthArray);

        var chunks = [];
        for (var i = 0; i < messageArray.length; i += 64) {
            chunks.push(messageArray.slice(i, i + 64));
        }

        for (var chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
            var chunk = chunks[chunkIndex];
            var aa = a;
            var bb = b;
            var cc = c;
            var dd = d;

            for (var i = 0; i < 64; i++) {
                var f, g;
                if (i < 16) {
                    f = (b & c) | (~b & d);
                    g = i;
                } else if (i < 32) {
                    f = (b & d) | (c & ~d);
                    g = (5 * i + 1) % 16;
                } else if (i < 48) {
                    f = b ^ c ^ d;
                    g = (3 * i + 5) % 16;
                } else {
                    f = c ^ (b | ~d);
                    g = (7 * i) % 16;
                }

                var temp = this.md5_add(this.md5_add(this.md5_leftRotate(this.md5_add(a, f), rotateAmounts[i]), constants[i]), chunk[g]);
                a = d;
                d = c;
                c = this.md5_leftRotate(b, 9);
                b = temp;
            }

            a = this.md5_add(a, aa);
            b = this.md5_add(b, bb);
            c = this.md5_add(c, cc);
            d = this.md5_add(d, dd);
        }

        return [a, b, c, d].map(function (value) {
            return ('00000000' + value.toString(16)).substr(-8);
        }).join('');
    }
    // 自定义加密函数
    customEncrypt(input) {
        let salt = this.appKey
        let result = '';
        if(!input) return result;
        for (let i = 0; i < input.length; i++) {
          let charCode = input.charCodeAt(i);
          // 加入盐值的影响，这里使用简单的异或操作
          charCode = charCode ^ salt.charCodeAt(i % salt.length); 
          result += String.fromCharCode(charCode);
        }
        return result;
    }
    // 自定义解密函数
    customDecrypt(encryptedInput) {
        let salt = this.appKey
        let result = '';
        if(!encryptedInput) return result;
        for (let i = 0; i < encryptedInput.length; i++) {
          let charCode = encryptedInput.charCodeAt(i);
          // 再次使用异或操作进行解密，因为异或操作的特性：a ^ b ^ b = a
          charCode = charCode ^ salt.charCodeAt(i % salt.length); 
          result += String.fromCharCode(charCode);
        }
        return result;
    }
    // 对象值进行加密
    encryptObjectValues(object) {
        let encryptedObj = {};
        if(object == null) return null;
        for (let key in object) {
          if (object.hasOwnProperty(key)) {
            let encrykey = this.customEncrypt(key)
            if (Array.isArray(object[key])) {
                // 如果是数组，递归调用 encryptObjectValues 处理数组元素
                encryptedObj[encrykey] = object[key].map(item => this.encryptObjectValues(item));
            } else if (typeof object[key] === 'object' && object[key]!== null) {
                // 如果是对象，递归调用 encryptObjectValues 处理子对象
                encryptedObj[encrykey] = this.encryptObjectValues(object[key]);
            } else {
                // 对值进行加密
                encryptedObj[encrykey] = this.customEncrypt(object[key]);
            }
          }
        }
        return encryptedObj;
    }
    // 递归遍历对象并解密值的函数
    decryptObjectValues(object) {
        let decryptedObj = {};
        if(object == null) return null;
        for (let key in object) {
            if (object.hasOwnProperty(key)) {
                let decrykey = this.customEncrypt(key)
                if (Array.isArray(object[key])) {
                    // 如果是数组，递归调用 decryptObjectValues 处理数组元素
                    decryptedObj[decrykey] = object[key].map(item => this.decryptObjectValues(item));
                } else if (typeof object[key] === 'object' && object[key]!== null) {
                    // 如果是对象，递归调用 decryptObjectValues 处理子对象
                    decryptedObj[decrykey] = this.decryptObjectValues(object[key]);
                } else {
                    // 对值进行解密
                    decryptedObj[decrykey] = this.customDecrypt(object[key]);
                }
            }
        }
        return decryptedObj;
    }
      
}

export default XappTool
